#include "structMesh_testFunctions.hpp"
#include "util.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>

using Catch::Approx;

using namespace hms;
using CellType = RectMesh::CellType;

TEST_CASE( "True rectCell: number of edges", "[omp]" ){

	auto test = []( const auto& mesh ){
			
		SECTION( "nEdges" ){
			mesh.forEachDomainCell([](const CellType& cell){
				REQUIRE( cell.nEdges() == 4 );
			});
			mesh.forEachGhostCell([](const CellType& cell){
				REQUIRE( cell.nEdges() == 1);
			});
		}
	};

	SECTION("One ghost layer"){
		test(createRectMesh());
	}
}

TEST_CASE( "True rectCell: cell area" ){
	auto testOneLayer = [](const auto& mesh){
		REQUIRE( mesh.cell( 1).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell( 2).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell( 3).area() == Approx( 2.25 ) );
		REQUIRE( mesh.cell( 5).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell( 6).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell( 7).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell( 8).area() == Approx( 2.25 ) );
		REQUIRE( mesh.cell( 9).area() == Approx( 2.25 ) );
		REQUIRE( mesh.cell(10).area() == Approx( 0.25 ) );
		REQUIRE( mesh.cell(11).area() == Approx( 0.25 ) );
		REQUIRE( mesh.cell(12).area() == Approx( 0.5  ) );
		REQUIRE( mesh.cell(13).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell(14).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell(15).area() == Approx( 0.5  ) );
		REQUIRE( mesh.cell(16).area() == Approx( 0.5  ) );
		REQUIRE( mesh.cell(17).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(18).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell(19).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell(20).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(21).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(22).area() == Approx( 2.0  ) );
		REQUIRE( mesh.cell(23).area() == Approx( 3.0  ) );
		REQUIRE( mesh.cell(24).area() == Approx( 3.0  ) );
		REQUIRE( mesh.cell(26).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(27).area() == Approx( 2.0  ) );
		REQUIRE( mesh.cell(28).area() == Approx( 3.0  ) );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( mesh.cell( 2).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell( 3).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell( 4).area() == Approx( 2.25 ) );
		REQUIRE( mesh.cell( 9).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell(10).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell(11).area() == Approx( 2.25 ) );

		REQUIRE( mesh.cell(14).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell(15).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell(16).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell(17).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell(18).area() == Approx( 2.25 ) );
		REQUIRE( mesh.cell(19).area() == Approx( 2.25 ) );
		REQUIRE( mesh.cell(20).area() == Approx( 2.25 ) );

		REQUIRE( mesh.cell(21).area() == Approx( 0.25 ) );
		REQUIRE( mesh.cell(22).area() == Approx( 0.25 ) );
		REQUIRE( mesh.cell(23).area() == Approx( 0.25 ) );
		REQUIRE( mesh.cell(24).area() == Approx( 0.5  ) );
		REQUIRE( mesh.cell(25).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell(26).area() == Approx( 0.75 ) );
		REQUIRE( mesh.cell(27).area() == Approx( 0.75 ) );

		REQUIRE( mesh.cell(28).area() == Approx( 0.5  ) );
		REQUIRE( mesh.cell(29).area() == Approx( 0.5  ) );
		REQUIRE( mesh.cell(30).area() == Approx( 0.5  ) );
		REQUIRE( mesh.cell(31).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(32).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell(33).area() == Approx( 1.5  ) );
		REQUIRE( mesh.cell(34).area() == Approx( 1.5  ) );

		REQUIRE( mesh.cell(35).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(36).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(37).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(38).area() == Approx( 2.0  ) );
		REQUIRE( mesh.cell(39).area() == Approx( 3.0  ) );
		REQUIRE( mesh.cell(40).area() == Approx( 3.0  ) );
		REQUIRE( mesh.cell(41).area() == Approx( 3.0  ) );

		REQUIRE( mesh.cell(44).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(45).area() == Approx( 2.0  ) );
		REQUIRE( mesh.cell(46).area() == Approx( 3.0  ) );
		REQUIRE( mesh.cell(51).area() == Approx( 1.0  ) );
		REQUIRE( mesh.cell(52).area() == Approx( 2.0  ) );
		REQUIRE( mesh.cell(53).area() == Approx( 3.0  ) );
	};

	SECTION("One ghost layer"){
		testOneLayer(createRectMesh());
	}
}

TEST_CASE( "True rectCell: circumference" ){
	auto testOneLayer = [](const auto& mesh){
		REQUIRE( mesh.cell( 6).circumf() == Approx( 2*0.5 + 2*1.5 ) );
		REQUIRE( mesh.cell( 7).circumf() == Approx( 2*1.0 + 2*1.5 ) );
		REQUIRE( mesh.cell( 8).circumf() == Approx( 2*1.5 + 2*1.5 ) );
		REQUIRE( mesh.cell(11).circumf() == Approx( 2*0.5 + 2*0.5 ) );
		REQUIRE( mesh.cell(12).circumf() == Approx( 2*1.0 + 2*0.5 ) );
		REQUIRE( mesh.cell(13).circumf() == Approx( 2*1.5 + 2*0.5 ) );
		REQUIRE( mesh.cell(16).circumf() == Approx( 2*0.5 + 2*1.0 ) );
		REQUIRE( mesh.cell(17).circumf() == Approx( 2*1.0 + 2*1.0 ) );
		REQUIRE( mesh.cell(18).circumf() == Approx( 2*1.5 + 2*1.0 ) );
		REQUIRE( mesh.cell(21).circumf() == Approx( 2*0.5 + 2*2.0 ) );
		REQUIRE( mesh.cell(22).circumf() == Approx( 2*1.0 + 2*2.0 ) );
		REQUIRE( mesh.cell(23).circumf() == Approx( 2*1.5 + 2*2.0 ) );

	};
	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( mesh.cell(16).circumf() == Approx( 2*0.5 + 2*1.5 ) );
		REQUIRE( mesh.cell(17).circumf() == Approx( 2*1.0 + 2*1.5 ) );
		REQUIRE( mesh.cell(18).circumf() == Approx( 2*1.5 + 2*1.5 ) );
		REQUIRE( mesh.cell(23).circumf() == Approx( 2*0.5 + 2*0.5 ) );
		REQUIRE( mesh.cell(24).circumf() == Approx( 2*1.0 + 2*0.5 ) );
		REQUIRE( mesh.cell(25).circumf() == Approx( 2*1.5 + 2*0.5 ) );
		REQUIRE( mesh.cell(30).circumf() == Approx( 2*0.5 + 2*1.0 ) );
		REQUIRE( mesh.cell(31).circumf() == Approx( 2*1.0 + 2*1.0 ) );
		REQUIRE( mesh.cell(32).circumf() == Approx( 2*1.5 + 2*1.0 ) );
		REQUIRE( mesh.cell(37).circumf() == Approx( 2*0.5 + 2*2.0 ) );
		REQUIRE( mesh.cell(38).circumf() == Approx( 2*1.0 + 2*2.0 ) );
		REQUIRE( mesh.cell(39).circumf() == Approx( 2*1.5 + 2*2.0 ) );

	};

	SECTION("One ghost layer"){
		testOneLayer(createRectMesh());
	}
}

TEST_CASE( "True rectCell: boundary and ghost flags", "[omp]" ){
	auto test = []( const auto& mesh ){
		Index m {mesh.nCellsX()};
		Index n {mesh.nCellsY()};

		mesh.forEachDomainCell([&](const CellType& cell){
			REQUIRE( !cell.isGhost() );
			if (cell.i()==0 || cell.i()==m-1 || cell.j()==0 || cell.j()==n-1)
				REQUIRE( cell.isBoundary() );
			else
				REQUIRE( !cell.isBoundary() );
		});
		mesh.forEachGhostCell([&](const CellType& cell){
			REQUIRE( !cell.isBoundary() );
			REQUIRE( cell.isGhost() );
		});
	};

	SECTION("One ghost layer"){
		test(createRectMesh());
	}
}

TEST_CASE( "True rectCell: centroids" ){
	using hms::approxEqual;

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( approxEqual( mesh.cell( 1).centroid(), Vector2s{  0.25 , -0.75 } ) );
		REQUIRE( approxEqual( mesh.cell( 2).centroid(), Vector2s{  1.0  , -0.75 } ) );
		REQUIRE( approxEqual( mesh.cell( 3).centroid(), Vector2s{  2.25 , -0.75 } ) );

		REQUIRE( approxEqual( mesh.cell( 5).centroid(), Vector2s{ -0.25 , 0.75 } ) );
		REQUIRE( approxEqual( mesh.cell( 6).centroid(), Vector2s{  0.25 , 0.75 } ) );
		REQUIRE( approxEqual( mesh.cell( 7).centroid(), Vector2s{  1.0  , 0.75 } ) );
		REQUIRE( approxEqual( mesh.cell( 8).centroid(), Vector2s{  2.25 , 0.75 } ) );
		REQUIRE( approxEqual( mesh.cell( 9).centroid(), Vector2s{  3.75 , 0.75 } ) );

		REQUIRE( approxEqual( mesh.cell(10).centroid(), Vector2s{ -0.25 , 1.75 } ) );
		REQUIRE( approxEqual( mesh.cell(11).centroid(), Vector2s{  0.25 , 1.75 } ) );
		REQUIRE( approxEqual( mesh.cell(12).centroid(), Vector2s{  1.0  , 1.75 } ) );
		REQUIRE( approxEqual( mesh.cell(13).centroid(), Vector2s{  2.25 , 1.75 } ) );
		REQUIRE( approxEqual( mesh.cell(14).centroid(), Vector2s{  3.75 , 1.75 } ) );

		REQUIRE( approxEqual( mesh.cell(15).centroid(), Vector2s{ -0.25 , 2.5  } ) );
		REQUIRE( approxEqual( mesh.cell(16).centroid(), Vector2s{  0.25 , 2.5  } ) );
		REQUIRE( approxEqual( mesh.cell(17).centroid(), Vector2s{  1.0  , 2.5  } ) );
		REQUIRE( approxEqual( mesh.cell(18).centroid(), Vector2s{  2.25 , 2.5  } ) );
		REQUIRE( approxEqual( mesh.cell(19).centroid(), Vector2s{  3.75 , 2.5  } ) );

		REQUIRE( approxEqual( mesh.cell(20).centroid(), Vector2s{ -0.25 , 4.0  } ) );
		REQUIRE( approxEqual( mesh.cell(21).centroid(), Vector2s{  0.25 , 4.0  } ) );
		REQUIRE( approxEqual( mesh.cell(22).centroid(), Vector2s{  1.0  , 4.0  } ) );
		REQUIRE( approxEqual( mesh.cell(23).centroid(), Vector2s{  2.25 , 4.0  } ) );
		REQUIRE( approxEqual( mesh.cell(24).centroid(), Vector2s{  3.75 , 4.0  } ) );

		REQUIRE( approxEqual( mesh.cell(26).centroid(), Vector2s{  0.25 , 6.0  } ) );
		REQUIRE( approxEqual( mesh.cell(27).centroid(), Vector2s{  1.0  , 6.0  } ) );
		REQUIRE( approxEqual( mesh.cell(28).centroid(), Vector2s{  2.25 , 6.0  } ) );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( approxEqual( mesh.cell( 2).centroid(), Vector2s{  0.25 , -2.25 } ) );
		REQUIRE( approxEqual( mesh.cell( 3).centroid(), Vector2s{  1.0  , -2.25 } ) );
		REQUIRE( approxEqual( mesh.cell( 4).centroid(), Vector2s{  2.25 , -2.25 } ) );
		REQUIRE( approxEqual( mesh.cell( 9).centroid(), Vector2s{  0.25 , -0.75 } ) );
		REQUIRE( approxEqual( mesh.cell(10).centroid(), Vector2s{  1.0  , -0.75 } ) );
		REQUIRE( approxEqual( mesh.cell(11).centroid(), Vector2s{  2.25 , -0.75 } ) );

		REQUIRE( approxEqual( mesh.cell(14).centroid(), Vector2s{ -0.75 , 0.75  } ) );
		REQUIRE( approxEqual( mesh.cell(15).centroid(), Vector2s{ -0.25 , 0.75  } ) );
		REQUIRE( approxEqual( mesh.cell(16).centroid(), Vector2s{  0.25 , 0.75  } ) );
		REQUIRE( approxEqual( mesh.cell(17).centroid(), Vector2s{  1.0  , 0.75  } ) );
		REQUIRE( approxEqual( mesh.cell(18).centroid(), Vector2s{  2.25 , 0.75  } ) );
		REQUIRE( approxEqual( mesh.cell(19).centroid(), Vector2s{  3.75 , 0.75  } ) );
		REQUIRE( approxEqual( mesh.cell(20).centroid(), Vector2s{  5.25 , 0.75  } ) );

		REQUIRE( approxEqual( mesh.cell(21).centroid(), Vector2s{ -0.75 , 1.75  } ) );
		REQUIRE( approxEqual( mesh.cell(22).centroid(), Vector2s{ -0.25 , 1.75  } ) );
		REQUIRE( approxEqual( mesh.cell(23).centroid(), Vector2s{  0.25 , 1.75  } ) );
		REQUIRE( approxEqual( mesh.cell(24).centroid(), Vector2s{  1.0  , 1.75  } ) );
		REQUIRE( approxEqual( mesh.cell(25).centroid(), Vector2s{  2.25 , 1.75  } ) );
		REQUIRE( approxEqual( mesh.cell(26).centroid(), Vector2s{  3.75 , 1.75  } ) );
		REQUIRE( approxEqual( mesh.cell(27).centroid(), Vector2s{  5.25 , 1.75  } ) );

		REQUIRE( approxEqual( mesh.cell(28).centroid(), Vector2s{ -0.75 , 2.5   } ) );
		REQUIRE( approxEqual( mesh.cell(29).centroid(), Vector2s{ -0.25 , 2.5   } ) );
		REQUIRE( approxEqual( mesh.cell(30).centroid(), Vector2s{  0.25 , 2.5   } ) );
		REQUIRE( approxEqual( mesh.cell(31).centroid(), Vector2s{  1.0  , 2.5   } ) );
		REQUIRE( approxEqual( mesh.cell(32).centroid(), Vector2s{  2.25 , 2.5   } ) );
		REQUIRE( approxEqual( mesh.cell(33).centroid(), Vector2s{  3.75 , 2.5   } ) );
		REQUIRE( approxEqual( mesh.cell(34).centroid(), Vector2s{  5.25 , 2.5   } ) );

		REQUIRE( approxEqual( mesh.cell(35).centroid(), Vector2s{ -0.75 , 4.0   } ) );
		REQUIRE( approxEqual( mesh.cell(36).centroid(), Vector2s{ -0.25 , 4.0   } ) );
		REQUIRE( approxEqual( mesh.cell(37).centroid(), Vector2s{  0.25 , 4.0   } ) );
		REQUIRE( approxEqual( mesh.cell(38).centroid(), Vector2s{  1.0  , 4.0   } ) );
		REQUIRE( approxEqual( mesh.cell(39).centroid(), Vector2s{  2.25 , 4.0   } ) );
		REQUIRE( approxEqual( mesh.cell(40).centroid(), Vector2s{  3.75 , 4.0   } ) );
		REQUIRE( approxEqual( mesh.cell(41).centroid(), Vector2s{  5.25 , 4.0   } ) );

		REQUIRE( approxEqual( mesh.cell(44).centroid(), Vector2s{  0.25 , 6.0   } ) );
		REQUIRE( approxEqual( mesh.cell(45).centroid(), Vector2s{  1.0  , 6.0   } ) );
		REQUIRE( approxEqual( mesh.cell(46).centroid(), Vector2s{  2.25 , 6.0   } ) );
		REQUIRE( approxEqual( mesh.cell(51).centroid(), Vector2s{  0.25 , 8.0   } ) );
		REQUIRE( approxEqual( mesh.cell(52).centroid(), Vector2s{  1.0  , 8.0   } ) );
		REQUIRE( approxEqual( mesh.cell(53).centroid(), Vector2s{  2.25 , 8.0   } ) );
	};

	SECTION("One ghost layer"){
		testOneLayer(createRectMesh());
	}
}

TEST_CASE( "True rectCell: neighbours" ){

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( (mesh.cell( 1).neighbours() ==  6 ).all() );
		REQUIRE( (mesh.cell( 2).neighbours() ==  7 ).all() );
		REQUIRE( (mesh.cell( 3).neighbours() ==  8 ).all() );
		
		REQUIRE( (mesh.cell( 5).neighbours() ==  6 ).all() );
		REQUIRE( (mesh.cell( 6).neighbours() == Array4i{  1,  7, 11,  5 }).all() );
		REQUIRE( (mesh.cell( 7).neighbours() == Array4i{  2,  8, 12,  6 }).all() );
		REQUIRE( (mesh.cell( 8).neighbours() == Array4i{  3,  9, 13,  7 }).all() );
		REQUIRE( (mesh.cell( 9).neighbours() ==  8 ).all() );
		REQUIRE( (mesh.cell(10).neighbours() == 11 ).all() );
		REQUIRE( (mesh.cell(11).neighbours() == Array4i{  6, 12, 16, 10 }).all() );
		REQUIRE( (mesh.cell(12).neighbours() == Array4i{  7, 13, 17, 11 }).all() );
		REQUIRE( (mesh.cell(13).neighbours() == Array4i{  8, 14, 18, 12 }).all() );
		REQUIRE( (mesh.cell(14).neighbours() == 13 ).all() );
		REQUIRE( (mesh.cell(15).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell(16).neighbours() == Array4i{ 11, 17, 21, 15 }).all() );
		REQUIRE( (mesh.cell(17).neighbours() == Array4i{ 12, 18, 22, 16 }).all() );
		REQUIRE( (mesh.cell(18).neighbours() == Array4i{ 13, 19, 23, 17 }).all() );
		REQUIRE( (mesh.cell(19).neighbours() == 18 ).all() );
		REQUIRE( (mesh.cell(20).neighbours() == 21 ).all() );
		REQUIRE( (mesh.cell(21).neighbours() == Array4i{ 16, 22, 26, 20 }).all() );
		REQUIRE( (mesh.cell(22).neighbours() == Array4i{ 17, 23, 27, 21 }).all() );
		REQUIRE( (mesh.cell(23).neighbours() == Array4i{ 18, 24, 28, 22 }).all() );
		REQUIRE( (mesh.cell(24).neighbours() == 23 ).all() );
		
		REQUIRE( (mesh.cell(26).neighbours() == 21 ).all() );
		REQUIRE( (mesh.cell(27).neighbours() == 22 ).all() );
		REQUIRE( (mesh.cell(28).neighbours() == 23 ).all() );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( (mesh.cell( 2).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell( 3).neighbours() == 17 ).all() );
		REQUIRE( (mesh.cell( 4).neighbours() == 18 ).all() );

		REQUIRE( (mesh.cell( 9).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell(10).neighbours() == 17 ).all() );
		REQUIRE( (mesh.cell(11).neighbours() == 18 ).all() );
		
		REQUIRE( (mesh.cell(14).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell(15).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell(16).neighbours() == Array4i{  9, 17, 23, 15 }).all() );
		REQUIRE( (mesh.cell(17).neighbours() == Array4i{ 10, 18, 24, 16 }).all() );
		REQUIRE( (mesh.cell(18).neighbours() == Array4i{ 11, 19, 25, 17 }).all() );
		REQUIRE( (mesh.cell(19).neighbours() == 18 ).all() );
		REQUIRE( (mesh.cell(20).neighbours() == 18 ).all() );
		REQUIRE( (mesh.cell(21).neighbours() == 23 ).all() );
		REQUIRE( (mesh.cell(22).neighbours() == 23 ).all() );
		REQUIRE( (mesh.cell(23).neighbours() == Array4i{ 16, 24, 30, 22 }).all() );
		REQUIRE( (mesh.cell(24).neighbours() == Array4i{ 17, 25, 31, 23 }).all() );
		REQUIRE( (mesh.cell(25).neighbours() == Array4i{ 18, 26, 32, 24 }).all() );
		REQUIRE( (mesh.cell(26).neighbours() == 25 ).all() );
		REQUIRE( (mesh.cell(27).neighbours() == 25 ).all() );
		REQUIRE( (mesh.cell(28).neighbours() == 30 ).all() );
		REQUIRE( (mesh.cell(29).neighbours() == 30 ).all() );
		REQUIRE( (mesh.cell(30).neighbours() == Array4i{ 23, 31, 37, 29 }).all() );
		REQUIRE( (mesh.cell(31).neighbours() == Array4i{ 24, 32, 38, 30 }).all() );
		REQUIRE( (mesh.cell(32).neighbours() == Array4i{ 25, 33, 39, 31 }).all() );
		REQUIRE( (mesh.cell(33).neighbours() == 32 ).all() );
		REQUIRE( (mesh.cell(34).neighbours() == 32 ).all() );
		REQUIRE( (mesh.cell(35).neighbours() == 37 ).all() );
		REQUIRE( (mesh.cell(36).neighbours() == 37 ).all() );
		REQUIRE( (mesh.cell(37).neighbours() == Array4i{ 30, 38, 44, 36 }).all() );
		REQUIRE( (mesh.cell(38).neighbours() == Array4i{ 31, 39, 45, 37 }).all() );
		REQUIRE( (mesh.cell(39).neighbours() == Array4i{ 32, 40, 46, 38 }).all() );
		REQUIRE( (mesh.cell(40).neighbours() == 39 ).all() );
		REQUIRE( (mesh.cell(41).neighbours() == 39 ).all() );

		REQUIRE( (mesh.cell(44).neighbours() == 37 ).all() );
		REQUIRE( (mesh.cell(45).neighbours() == 38 ).all() );
		REQUIRE( (mesh.cell(46).neighbours() == 39 ).all() );

		REQUIRE( (mesh.cell(51).neighbours() == 37 ).all() );
		REQUIRE( (mesh.cell(52).neighbours() == 38 ).all() );
		REQUIRE( (mesh.cell(53).neighbours() == 39 ).all() );
	};

	SECTION("One ghost layer"){
		testOneLayer(createRectMesh());
	}
}

TEST_CASE( "True rectCell: vertices" ){
	using hms::exactlyEqual;

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( exactlyEqual(mesh.cell( 1).vertices(), Array2i{  0,  1 }) );
		REQUIRE( exactlyEqual(mesh.cell( 2).vertices(), Array2i{  1,  2 }) );
		REQUIRE( exactlyEqual(mesh.cell( 3).vertices(), Array2i{  2,  3 }) );
		
		REQUIRE( exactlyEqual(mesh.cell( 5).vertices(), Array2i{  0,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell( 6).vertices(), Array4i{  0,  1,  5,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell( 7).vertices(), Array4i{  1,  2,  6,  5 }) );
		REQUIRE( exactlyEqual(mesh.cell( 8).vertices(), Array4i{  2,  3,  7,  6 }) );
		REQUIRE( exactlyEqual(mesh.cell( 9).vertices(), Array2i{  3,  7 }) );
		REQUIRE( exactlyEqual(mesh.cell(10).vertices(), Array2i{  4,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(11).vertices(), Array4i{  4,  5,  9,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(12).vertices(), Array4i{  5,  6, 10,  9 }) );
		REQUIRE( exactlyEqual(mesh.cell(13).vertices(), Array4i{  6,  7, 11, 10 }) );
		REQUIRE( exactlyEqual(mesh.cell(14).vertices(), Array2i{  7, 11 }) );
		REQUIRE( exactlyEqual(mesh.cell(15).vertices(), Array2i{  8, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(16).vertices(), Array4i{  8,  9, 13, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(17).vertices(), Array4i{  9, 10, 14, 13 }) );
		REQUIRE( exactlyEqual(mesh.cell(18).vertices(), Array4i{ 10, 11, 15, 14 }) );
		REQUIRE( exactlyEqual(mesh.cell(19).vertices(), Array2i{ 11, 15 }) );
		REQUIRE( exactlyEqual(mesh.cell(20).vertices(), Array2i{ 12, 16 }) );
		REQUIRE( exactlyEqual(mesh.cell(21).vertices(), Array4i{ 12, 13, 17, 16 }) );
		REQUIRE( exactlyEqual(mesh.cell(22).vertices(), Array4i{ 13, 14, 18, 17 }) );
		REQUIRE( exactlyEqual(mesh.cell(23).vertices(), Array4i{ 14, 15, 19, 18 }) );
		REQUIRE( exactlyEqual(mesh.cell(24).vertices(), Array2i{ 15, 19 }) );
		
		REQUIRE( exactlyEqual(mesh.cell(26).vertices(), Array2i{ 16, 17 }) );
		REQUIRE( exactlyEqual(mesh.cell(27).vertices(), Array2i{ 17, 18 }) );
		REQUIRE( exactlyEqual(mesh.cell(28).vertices(), Array2i{ 18, 19 }) );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( exactlyEqual(mesh.cell( 2).vertices(), Array2i{  0,  1 }) );
		REQUIRE( exactlyEqual(mesh.cell( 3).vertices(), Array2i{  1,  2 }) );
		REQUIRE( exactlyEqual(mesh.cell( 4).vertices(), Array2i{  2,  3 }) );

		REQUIRE( exactlyEqual(mesh.cell( 9).vertices(), Array2i{  0,  1 }) );
		REQUIRE( exactlyEqual(mesh.cell(10).vertices(), Array2i{  1,  2 }) );
		REQUIRE( exactlyEqual(mesh.cell(11).vertices(), Array2i{  2,  3 }) );
		
		REQUIRE( exactlyEqual(mesh.cell(14).vertices(), Array2i{  0,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell(15).vertices(), Array2i{  0,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell(16).vertices(), Array4i{  0,  1,  5,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell(17).vertices(), Array4i{  1,  2,  6,  5 }) );
		REQUIRE( exactlyEqual(mesh.cell(18).vertices(), Array4i{  2,  3,  7,  6 }) );
		REQUIRE( exactlyEqual(mesh.cell(19).vertices(), Array2i{  3,  7 }) );
		REQUIRE( exactlyEqual(mesh.cell(20).vertices(), Array2i{  3,  7 }) );
		REQUIRE( exactlyEqual(mesh.cell(21).vertices(), Array2i{  4,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(22).vertices(), Array2i{  4,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(23).vertices(), Array4i{  4,  5,  9,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(24).vertices(), Array4i{  5,  6, 10,  9 }) );
		REQUIRE( exactlyEqual(mesh.cell(25).vertices(), Array4i{  6,  7, 11, 10 }) );
		REQUIRE( exactlyEqual(mesh.cell(26).vertices(), Array2i{  7, 11 }) );
		REQUIRE( exactlyEqual(mesh.cell(27).vertices(), Array2i{  7, 11 }) );
		REQUIRE( exactlyEqual(mesh.cell(28).vertices(), Array2i{  8, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(29).vertices(), Array2i{  8, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(30).vertices(), Array4i{  8,  9, 13, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(31).vertices(), Array4i{  9, 10, 14, 13 }) );
		REQUIRE( exactlyEqual(mesh.cell(32).vertices(), Array4i{ 10, 11, 15, 14 }) );
		REQUIRE( exactlyEqual(mesh.cell(33).vertices(), Array2i{ 11, 15 }) );
		REQUIRE( exactlyEqual(mesh.cell(34).vertices(), Array2i{ 11, 15 }) );
		REQUIRE( exactlyEqual(mesh.cell(35).vertices(), Array2i{ 12, 16 }) );
		REQUIRE( exactlyEqual(mesh.cell(36).vertices(), Array2i{ 12, 16 }) );
		REQUIRE( exactlyEqual(mesh.cell(37).vertices(), Array4i{ 12, 13, 17, 16 }) );
		REQUIRE( exactlyEqual(mesh.cell(38).vertices(), Array4i{ 13, 14, 18, 17 }) );
		REQUIRE( exactlyEqual(mesh.cell(39).vertices(), Array4i{ 14, 15, 19, 18 }) );
		REQUIRE( exactlyEqual(mesh.cell(40).vertices(), Array2i{ 15, 19 }) );
		REQUIRE( exactlyEqual(mesh.cell(41).vertices(), Array2i{ 15, 19 }) );

		REQUIRE( exactlyEqual(mesh.cell(44).vertices(), Array2i{ 16, 17 }) );
		REQUIRE( exactlyEqual(mesh.cell(45).vertices(), Array2i{ 17, 18 }) );
		REQUIRE( exactlyEqual(mesh.cell(46).vertices(), Array2i{ 18, 19 }) );

		REQUIRE( exactlyEqual(mesh.cell(51).vertices(), Array2i{ 16, 17 }) );
		REQUIRE( exactlyEqual(mesh.cell(52).vertices(), Array2i{ 17, 18 }) );
		REQUIRE( exactlyEqual(mesh.cell(53).vertices(), Array2i{ 18, 19 }) );
	};

	SECTION("One ghost layer"){
		testOneLayer(createRectMesh());
	}
}

TEST_CASE( "True rectCell: edges" ){
	using hms::exactlyEqual;

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( mesh.cell( 1).edge() == 0 );
		REQUIRE( mesh.cell( 2).edge() == 1 );
		REQUIRE( mesh.cell( 3).edge() == 2 );
		
		REQUIRE( mesh.cell( 5).edge() == 15);
		REQUIRE( exactlyEqual(mesh.cell( 6).edges(), Array4i{  0, 16,  3, 15 } ) );
		REQUIRE( exactlyEqual(mesh.cell( 7).edges(), Array4i{  1, 17,  4, 16 } ) );
		REQUIRE( exactlyEqual(mesh.cell( 8).edges(), Array4i{  2, 18,  5, 17 } ) );
		REQUIRE( mesh.cell( 9).edge() == 18);
		REQUIRE( mesh.cell(10).edge() == 19);
		REQUIRE( exactlyEqual(mesh.cell(11).edges(), Array4i{  3, 20,  6, 19 } ) );
		REQUIRE( exactlyEqual(mesh.cell(12).edges(), Array4i{  4, 21,  7, 20 } ) );
		REQUIRE( exactlyEqual(mesh.cell(13).edges(), Array4i{  5, 22,  8, 21 } ) );
		REQUIRE( mesh.cell(14).edge() == 22);
		REQUIRE( mesh.cell(15).edge() == 23);
		REQUIRE( exactlyEqual(mesh.cell(16).edges(), Array4i{  6, 24,  9, 23 } ) );
		REQUIRE( exactlyEqual(mesh.cell(17).edges(), Array4i{  7, 25, 10, 24 } ) );
		REQUIRE( exactlyEqual(mesh.cell(18).edges(), Array4i{  8, 26, 11, 25 } ) );
		REQUIRE( mesh.cell(19).edge() == 26);
		REQUIRE( mesh.cell(20).edge() == 27);
		REQUIRE( exactlyEqual(mesh.cell(21).edges(), Array4i{  9, 28, 12, 27 } ) );
		REQUIRE( exactlyEqual(mesh.cell(22).edges(), Array4i{ 10, 29, 13, 28 } ) );
		REQUIRE( exactlyEqual(mesh.cell(23).edges(), Array4i{ 11, 30, 14, 29 } ) );
		REQUIRE( mesh.cell(24).edge() == 30);
		
		REQUIRE( mesh.cell(26).edge() == 12);
		REQUIRE( mesh.cell(27).edge() == 13);
		REQUIRE( mesh.cell(28).edge() == 14);
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( mesh.cell( 2).edge () == 0 );
		REQUIRE( mesh.cell( 3).edge () == 1 );
		REQUIRE( mesh.cell( 4).edge () == 2 );

		REQUIRE( mesh.cell( 9).edge () == 0 );
		REQUIRE( mesh.cell(10).edge () == 1 );
		REQUIRE( mesh.cell(11).edge () == 2 );
		
		REQUIRE( mesh.cell(14).edge () == 15);
		REQUIRE( mesh.cell(15).edge () == 15);
		REQUIRE( exactlyEqual(mesh.cell(16).edges(), Array4i{  0, 16,  3, 15 } ) );
		REQUIRE( exactlyEqual(mesh.cell(17).edges(), Array4i{  1, 17,  4, 16 } ) );
		REQUIRE( exactlyEqual(mesh.cell(18).edges(), Array4i{  2, 18,  5, 17 } ) );
		REQUIRE( mesh.cell(19).edge () == 18);
		REQUIRE( mesh.cell(20).edge () == 18);
		REQUIRE( mesh.cell(21).edge () == 19);
		REQUIRE( mesh.cell(22).edge () == 19);
		REQUIRE( exactlyEqual(mesh.cell(23).edges(), Array4i{  3, 20,  6, 19 } ) );
		REQUIRE( exactlyEqual(mesh.cell(24).edges(), Array4i{  4, 21,  7, 20 } ) );
		REQUIRE( exactlyEqual(mesh.cell(25).edges(), Array4i{  5, 22,  8, 21 } ) );
		REQUIRE( mesh.cell(26).edge () == 22);
		REQUIRE( mesh.cell(27).edge () == 22);
		REQUIRE( mesh.cell(28).edge () == 23);
		REQUIRE( mesh.cell(29).edge () == 23);
		REQUIRE( exactlyEqual(mesh.cell(30).edges(), Array4i{  6, 24,  9, 23 } ) );
		REQUIRE( exactlyEqual(mesh.cell(31).edges(), Array4i{  7, 25, 10, 24 } ) );
		REQUIRE( exactlyEqual(mesh.cell(32).edges(), Array4i{  8, 26, 11, 25 } ) );
		REQUIRE( mesh.cell(33).edge () == 26);
		REQUIRE( mesh.cell(34).edge () == 26);
		REQUIRE( mesh.cell(35).edge () == 27);
		REQUIRE( mesh.cell(36).edge () == 27);
		REQUIRE( exactlyEqual(mesh.cell(37).edges(), Array4i{  9, 28, 12, 27 } ) );
		REQUIRE( exactlyEqual(mesh.cell(38).edges(), Array4i{ 10, 29, 13, 28 } ) );
		REQUIRE( exactlyEqual(mesh.cell(39).edges(), Array4i{ 11, 30, 14, 29 } ) );
		REQUIRE( mesh.cell(40).edge () == 30);
		REQUIRE( mesh.cell(41).edge () == 30);

		REQUIRE( mesh.cell(44).edge () == 12);
		REQUIRE( mesh.cell(45).edge () == 13);
		REQUIRE( mesh.cell(46).edge () == 14);

		REQUIRE( mesh.cell(51).edge () == 12);
		REQUIRE( mesh.cell(52).edge () == 13);
		REQUIRE( mesh.cell(53).edge () == 14);
	};

	SECTION("One ghost layer"){
		testOneLayer(createRectMesh());
	}
}
